library IEEE;
use IEEE.STD_LOGIC_ARITH.all;
use IEEE.STD_LOGIC_1164.all;
use IEEE.STD_LOGIC_UNSIGNED.all;

entity processor is
port(
	Clk         : in  std_logic;
	Reset       : in  std_logic;
	-- Instruction memory
	I_Addr      : out std_logic_vector(31 downto 0);
	I_RdStb     : out std_logic;
	I_WrStb     : out std_logic;
	I_DataOut   : out std_logic_vector(31 downto 0);
	I_DataIn    : in  std_logic_vector(31 downto 0);
	-- Data memory
	D_Addr      : out std_logic_vector(31 downto 0);
	D_RdStb     : out std_logic;
	D_WrStb     : out std_logic;
	D_DataOut   : out std_logic_vector(31 downto 0);
	D_DataIn    : in  std_logic_vector(31 downto 0)
);
end processor;

architecture processor_arq of processor is 

-----------------
-- Componentes --
-----------------

	component uc is
		port(
			instruccion	: in	std_logic_vector(31 downto 26);
			regDst		: out	std_logic;
			aluSrc		: out	std_logic;
			memToReg	: out	std_logic;
			regWrite	: out	std_logic;
			memRead		: out	std_logic;
			memWrite	: out	std_logic;
			branch		: out	std_logic;
			aluOp		: out	std_logic_vector(1 downto 0)
		);
	end component;

	component alucontrol is
		port(
			output	: out	std_logic_vector(2 downto 0);
			aluOp	: in	std_logic_vector(1 downto 0);
			funct	: in	std_logic_vector(5 downto 0)
		);
	end component;

	component alu is
		port(
			a		:	in	std_logic_vector(31 downto 0);
			b		:	in	std_logic_vector(31 downto 0);
			control	:	in	std_logic_vector(2 downto 0);
			result	:	out	std_logic_vector(31 downto 0);
			zero	:	out	std_logic
		);
	end component;

	component Registers is
		Port ( reg1_rd : in  STD_LOGIC_VECTOR (4 downto 0);
			   reg2_rd : in  STD_LOGIC_VECTOR (4 downto 0);
			   reg_wr : in  STD_LOGIC_VECTOR (4 downto 0);
			   data_wr : in  STD_LOGIC_VECTOR (31 downto 0);
			   clk : in  STD_LOGIC;
			   reset : in  STD_LOGIC;
			   wr : in  STD_LOGIC;
			   data1_rd : out  STD_LOGIC_VECTOR (31 downto 0);
			   data2_rd : out  STD_LOGIC_VECTOR (31 downto 0));
	end component;

-------------
-- Señales --
-------------

	-- uc
	signal regDst		: std_logic;
	signal aluSrc		: std_logic;
	signal memToReg		: std_logic;
	signal regWrite		: std_logic;
	signal memRead		: std_logic;
	signal memWrite		: std_logic;
	signal branch		: std_logic;
	signal aluOp		: std_logic_vector(1 downto 0);

	-- alucontrol
	signal output		: std_logic_vector(2 downto 0);

	-- alu
	signal result		: std_logic_vector(31 downto 0);
	signal zero			: std_logic;

	-- Registers
	signal data1_rd		: std_logic_vector(31 downto 0);
	signal data2_rd		: std_logic_vector(31 downto 0);

	-- Multiplexores
	signal mux_regDst	: std_logic_vector(4 downto 0);
	signal mux_aluSrc	: std_logic_vector(31 downto 0);
	signal mux_memToReg	: std_logic_vector(31 downto 0);
	signal mux_FuentePC	: std_logic_vector(31 downto 0);

	signal extensionDeSigno : std_logic_vector(31 downto 0);

	-- PC
	signal pc : std_logic_vector(31 downto 0);

---------------------
-- begin port maps --
---------------------

begin

	uc_instance : uc
	port map(
		instruccion	=> I_DataIn(31 downto 26),
		regDst		=> regDst,
		aluSrc		=> aluSrc,
		memToReg	=> memToReg,
		regWrite	=> regWrite,
		memRead		=> D_RdStb,
		memWrite	=> D_WrStb,
		branch		=> branch,
		aluOp		=> aluOp(1 downto 0)
	);
	
	alucontrol_instance : alucontrol
	port map(
		output		=> output,
		aluOp		=> aluOp(1 downto 0),
		funct		=> I_DataIn(5 downto 0)
	);

	alu_instance : alu
	port map(
		a			=> data1_rd,
		b			=> mux_aluSrc,
		control		=> output,
		result		=> result,
		zero		=> zero
	);

	Registers_instance : Registers
	port map(
		reg1_rd		=> I_DataIn(25 downto 21),
		reg2_rd		=> I_DataIn(20 downto 16),
		reg_wr		=> mux_regDst,
		data_wr		=> mux_memToReg,
		clk			=> Clk,
		reset		=> Reset,
		wr			=> regWrite,
		data1_rd	=> data1_rd,
		data2_rd	=> data2_rd
	);

	mux_regDst		<= I_DataIn(15 downto 11) when regDst = '1' else I_DataIn(20 downto 16);
	mux_aluSrc		<= extensionDeSigno when aluSrc = '1' else data2_rd;
	mux_memToReg	<= D_DataIn when memToReg = '1' else result;
	mux_FuentePC	<= extensionDeSigno(29 downto 0) & "00" + pc + 4 when (branch = '1' and zero = '1') else pc + 4;

	extensionDeSigno <= x"0000" & I_DataIn(15 downto 0) when (I_DataIn(15 downto 0) >= 0) else x"1111" & I_DataIn(15 downto 0);

	-- Conexión a Memoria de datos
	D_Addr		<= result;
	D_DataOut	<= data2_rd;

	--------
	-- PC --
	--------

	process(Clk, Reset) begin

		if (Reset = '1') then
			pc <= x"00000000";
		else 
			if RISING_EDGE(Clk) then
				pc <= mux_FuentePC;
			end if;
		end if;

	end process;

	I_Addr	<= pc;
	I_RdStb	<= '1';

end processor_arq;
